package main

import (
	"fmt"
	"gitee.com/kennyzw/go-sip-ua/pkg/session"
	"gitee.com/kennyzw/go-sip-ua/pkg/stack"
	"gitee.com/kennyzw/go-sip-ua/pkg/ua"
	"gitee.com/kennyzw/go-sip-ua/pkg/utils"
	"gitee.com/kennyzw/gosip/log"
	"gitee.com/kennyzw/gosip/sip"
	"gitee.com/kennyzw/gosip/transport"
)

type HijackCall struct {
	src *session.Session // just say bye to source
}

func (b *HijackCall) ToString() string {
	return b.src.Contact()
}

// B2BUA, 一个b2bua代表了一个完整呼叫..
type HijackUA struct {
	stack    *stack.SipStack
	ua       *ua.UserAgent
	accounts map[string]string
	domains  []string
	calls    []*HijackCall // all call from souce.

	// 不用注册直接回响应
	// registry registry.Registry // 缓存用.
}

var (
	logger log.Logger
)

func init() {
	logger = utils.NewLogrusLogger(log.InfoLevel, "HijackUA", nil)
}

//NewB2BUA .
func NewHijackUA(host string) *HijackUA {
	b := &HijackUA{
		// registry: registry.Registry(registry.NewMemoryRegistry()),
		accounts: make(map[string]string),
	}

	// TODO: host先写死.
	stack := stack.NewSipStack(&stack.SipStackConfig{
		Host:       host,
		UserAgent:  "Go B2BUA/1.0.0",
		Extensions: []string{"replaces", "outbound"},
		Dns:        "8.8.8.8",
		ServerAuthManager: stack.ServerAuthManager{
			Authenticator:     nil,
			RequiresChallenge: b.requiresChallenge,
		},
	})

	stack.OnConnectionError(b.handleConnectionError)

	// udp 和tcp监听9010.
	if err := stack.Listen("udp", "0.0.0.0:9010"); err != nil {
		logger.Panic(err)
	}
	if err := stack.Listen("tcp", "0.0.0.0:9010"); err != nil {
		logger.Panic(err)
	}

	// 一个stack只有一个ua实例.（代表整个uac和uas行为）.
	ua := ua.NewUserAgent(&ua.UserAgentConfig{
		SipStack: stack,
	})

	// InviteStateHandler监听所有请求引起的状态变更（发送和接受的）.
	// 包括HijackUa的所有请求响应.
	ua.InviteStateHandler = func(sess *session.Session, req *sip.Request, resp *sip.Response, state session.Status) {
		logger.Infof("InviteStateHandler: state => %v, type => %s", state, sess.Direction())

		switch state {
		// Handle incoming call.
		case session.InviteReceived: // 收到Invite.
			//to, _ := (*req).To()
			//from, _ := (*req).From()
			//caller := from.Address
			//called := to.Address

			// Could not found any records
			// sess.Reject(500, fmt.Sprintf("from %v to %v server error.", caller, called))
			sess.Reject(500, "Server Internal Error")

			// 直接删除.
			b.removeCall(sess)

		// Handle re-INVITE or UPDATE.
		case session.ReInviteReceived:
			logger.Infof("re-INVITE")
			switch sess.Direction() {
			case session.Incoming:
				sess.Accept(200)
			case session.Outgoing: // 主动发送update.
				//TODO: Need to provide correct answer.
			}

		case session.Canceled:
			fallthrough
		case session.Terminated:
			//TODO: 需要兼容协议栈超时...
			// call := b.findCall(sess)
			//if call != nil {
			//	if call.src == sess {
			//		call.dest.End()
			//	} else if call.dest == sess {
			//		call.src.End()
			//	}
			//}
			b.removeCall(sess)

		}
	}

	// register消息打印下
	//ua.RegisterStateHandler = func(state account.RegisterState) {
	//	logger.Infof("RegisterStateHandler: state => %v", state)
	//}

	// stack.OnRequest(sip.REGISTER, b.handleRegister)
	b.stack = stack
	b.ua = ua
	return b
}

func (b *HijackUA) Calls() []*HijackCall {
	return b.calls
}

// source一样的就是重复的.
func (b *HijackUA) findCall(sess *session.Session) *HijackCall {
	for _, call := range b.calls {
		if call.src == sess {
			return call
		}
	}
	return nil
}

func (b *HijackUA) removeCall(sess *session.Session) {
	for idx, call := range b.calls {
		if call.src == sess {
			b.calls = append(b.calls[:idx], b.calls[idx+1:]...)
			return
		}
	}
}

//Shutdown .
func (b *HijackUA) Shutdown() {
	b.ua.Shutdown()
}

func (b *HijackUA) requiresChallenge(req sip.Request) bool {
	switch req.Method() {
	case sip.INVITE:
		return true
	//case sip.RREFER:
	//	return false
	case sip.CANCEL:
		return false
	case sip.BYE:
		{
			// Allow locally initiated dialogs
			// Return false if call-id in sessions.
			return false
		}
	}
	return false
}

//AddAccount .
func (b *HijackUA) AddAccount(username string, password string) {
	b.accounts[username] = password
}

//GetAccounts .
func (b *HijackUA) GetAccounts() map[string]string {
	return b.accounts
}

func (b *HijackUA) requestCredential(username string) (string, string, error) {
	if password, found := b.accounts[username]; found {
		logger.Infof("Found user %s", username)
		return password, "", nil
	}
	return "", "", fmt.Errorf("username [%s] not found", username)
}

func (b *HijackUA) handleConnectionError(connError *transport.ConnectionError) {
	logger.Debugf("Handle Connection Lost: Source: %v, Dest: %v, Network: %v", connError.Source, connError.Dest, connError.Net)
	// b.registry.HandleConnectionError(connError)
}

func (b *HijackUA) SetLogLevel(level log.Level) {
	utils.SetLogLevel("HijackUA", level)
}
